/**
 * 上海中赢金融信息服务有限公司
 * Copyright (c) 2016-2026 hmc,Inc.All Rights Reserved.
 */
package com.sys.midware.mongo.core;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.sys.midware.assist.utils.Assert;
import com.sys.midware.logger.Logger;
import com.sys.midware.logger.LoggerFactory;
import com.sys.midware.mongo.config.MongoConfig;
import com.sys.midware.mongo.config.MongoConfigHolder;
import com.sys.midware.mongo.config.MongoConfigUtil;

/**
 * mongodb获取连接管理类 <b>ClassName：</b> MongodbManager <br/>
 * <b>Description：</b> mongodb管理类 <br/>
 * <b>@author：</b> mobing <br/>
 * <b>@date：</b> 2015年10月20日 下午4:49:42 <br/>
 * <b>@version: </b> <br/>
 */
public class MongoConnection2X {
    private static final Logger logger = LoggerFactory.getLogger(MongoConnection2X.class);

    private final static ConcurrentMap<String, Mongo> mongos = new ConcurrentHashMap<String, Mongo>();

    private final static ConcurrentMap<String, MongoConfig> myMongoConfigs = new ConcurrentHashMap<String, MongoConfig>();
    static {
        // 初始化Mongo配置
        MongoConfigUtil.initConfig();
        // 如果没有传nodename，则随机取一个mongo配置
        Map<String, MongoConfig> mogoConfigMap = MongoConfigHolder.get();
        MongoConfig mongoConfig = null;
        for (Entry<String, MongoConfig> map : mogoConfigMap.entrySet()) {
            mongoConfig = map.getValue();
            mongos.putIfAbsent(map.getKey(), initMongoClient(mongoConfig));
            myMongoConfigs.putIfAbsent(map.getKey(), mongoConfig);
        }
    }

    public static MongoConfig getMyMongoConfig(String name) {
        return myMongoConfigs.get(name);
    }

    public static MongoConfig getMyMongoConfig() {
        MongoConfig mongoConfig = null;
        for (Entry<String, MongoConfig> map : myMongoConfigs.entrySet()) {
            mongoConfig = map.getValue();
            if (mongoConfig != null) {
                break;
            }
        }
        return mongoConfig;
    }

    public static Mongo getMongo(String name) {
        return mongos.get(name);
    }

    public static Mongo getMongo() {
        Mongo mongo = null;
        for (Entry<String, Mongo> map : mongos.entrySet()) {
            mongo = map.getValue();
            if (mongo != null) {
                break;
            }
        }
        return mongo;

    }

    private static MongoClientOptions getConfOptions() {
        return new MongoClientOptions.Builder().socketKeepAlive(true) // 是否保持长链接
                .connectTimeout(5000) // 链接超时时间
                .socketTimeout(5000) // read数据超时时间
                .readPreference(ReadPreference.primary()) // 最近优先策略
                .connectionsPerHost(30) // 每个地址最大请求数
                .maxWaitTime(1000 * 60 * 2) // 长链接的最大等待时间
                .threadsAllowedToBlockForConnectionMultiplier(50) // 一个socket最大的等待请求数
                .writeConcern(WriteConcern.NORMAL).build();
    }

    /**
     * 描述:初始化mongoclient
     * 
     * @param
     * @return void
     */
    private static Mongo initMongoClient(MongoConfig mc) {
        if (mc == null) {
            Assert.notNull(mc, "mongoconfig is not null!");
        }
        Mongo mongoClient = null;
        // int maxWaitTime = mc.getMaxWaitTime();
        // int maxThreadCount =
        // mc.getThreadsAllowedToBlockForConnectionMultiplier();
        // connectionPool
        try {
            // MongoOptions mongoOption = new MongoOptions();
            // mongoOption.setAutoConnectRetry(mc.isAutoConnectRetry());
            // mongoOption.setConnectionsPerHost(mc.getConnectionPoolSize());
            // mongoOption.setMaxWaitTime(maxWaitTime);
            // mongoOption.setThreadsAllowedToBlockForConnectionMultiplier(maxThreadCount);
            ServerAddress address = new ServerAddress(mc.getHost(), mc.getPort());

            if (mc.isAuth()) {
                MongoCredential credential = MongoCredential.createCredential(mc.getUserName(), mc.getAuthDB(),
                        mc.getPassword().toCharArray());
                mongoClient = new MongoClient(address, Arrays.asList(credential), getConfOptions());
            } else {
                mongoClient = new MongoClient(address, getConfOptions());
            }

            // MongoClient mcc =new MongoClient();
            //
            // mongoClient = new Mongo(address, mongoOption);
        } catch (Exception e) {
            logger.error("mongoClient 初始化异常", e);
        }
        return mongoClient;
    }

    /**
     * 获取所有数据库
     * 
     * @param
     * @return List<String>
     */
    public static List<String> getAllDataBaseName() {

        return getMongo().getDatabaseNames();
    }

    /**
     * 获取所有数据库
     * 
     * @param
     * @return List<String>
     */
    public static List<String> getAllDataBaseName(String nodeName) {

        return getMongo(nodeName).getDatabaseNames();
    }

    /**
     * 获取DB
     * 
     * @param
     * @return DB
     */
    public static DB getDB(String dbname) {
        DB db = null;
        try {
            db = getMongo().getDB(dbname);
            // MongoConfig myMongoConfig = getMyMongoConfig();
            //
            // if (myMongoConfig.isAuth() && !db.isAuthenticated()) {
            // db.authenticate(myMongoConfig.getUserName(),
            // myMongoConfig.getPassword().toCharArray());
            // }
        } catch (Exception e) {
            logger.error("get mongodb fail:" + dbname, e);
        }
        return db;
    }

    /**
     * 获取DB
     * 
     * @param
     * @return DB
     */
    public static DB getDB(String nodeName, String dbname) {
        DB db = null;
        try {
            db = getMongo(nodeName).getDB(dbname);
//            MongoConfig myMongoConfig = getMyMongoConfig(nodeName);
//            if (myMongoConfig.isAuth() && !db.isAuthenticated()) {
//                db.authenticate(myMongoConfig.getUserName(), myMongoConfig.getPassword().toCharArray());
//            }
        } catch (Exception e) {
            logger.error("get mongodb fail:" + dbname, e);
        }
        return db;
    }

    /**
     * monitor the connection pool
     * 
     * @author peter.pan
     *
     */
    static class MonitorThread extends Thread {
        int i = 0;

        MonitorThread() {
        }

        public void run() {
            // while (true) {
            try {
                TimeUnit.SECONDS.sleep(1);
                logger.info("MongoDB MonitorThread, connection pool count:" + mongos.size());
                i++;
                for (Map.Entry<String, Mongo> entity : mongos.entrySet()) {
                    logger.info(entity.getKey() + "," + entity.getValue());
                }

            } catch (Throwable e) {
                e.printStackTrace();
            }
            // }
        }
    }

    // monitor thread pool
    static {
        Thread thread = new MonitorThread();
        thread.setDaemon(true);
        thread.start();
        addShutdownHook();
    }

    /**
     * Add shutdown hook.
     */
    private static void addShutdownHook() {
        // create shutdown hook
        Runnable shutdownHook = new Runnable() {
            public void run() {
                shutdown();
            }
        };
        // add shutdown hook
        Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
    }

    public static void shutdown() {
        for (Map.Entry<String, Mongo> entry : mongos.entrySet()) {
            entry.getValue().close();
            logger.info(entry.getKey() + ",mongodb connetion pool closed.");
        }
    }

}
